#pragma once
#include <memory>
#include "VGQSeries.h"
#include "VGQLeftSeries.h"
#include "VGQRightSeries.h"
#include "VGQAsympExp.h"
#include "CFraction.h"
#include <string>
#include "VarianceGammaDistribution.h"
#include "QuantileApprox.h"

class VGQApprox : public QuantileApprox
{
public:
	VGQApprox(double mu, double lambda, double alpha, double beta, double eps = std::numeric_limits<float>::epsilon()/*2.98e-8*/);
	virtual ~VGQApprox(void);

	virtual double operator()(double u);
	void profile(const std::string& directoryPath);

protected:
	double _tauL, _tauR, _uL, _uR, _u0, _eps, _mu; 
	
	// Order important here, we want the distribution to be initialized first
	std::shared_ptr<VarianceGammaDistribution> _pvgDistribution;
	std::shared_ptr<CFraction> _approxCL;
	std::shared_ptr<CFraction> _approxCR;
	std::shared_ptr<CFraction> _approxL;
	std::shared_ptr<CFraction> _approxR;
	
	shared_ptr<VGQSeries> _pcentralLSeries;
	shared_ptr<VGQSeries> _pcentralRSeries;
	shared_ptr<VGQLeftSeries> _pleftSeries;
	shared_ptr<VGQRightSeries> _prightSeries;
	
	double uError(double u, double xApprox);

	VGQAsympExp _asymptoticExpansion;

private:
	double _maxAccuracy;
	const unsigned int _maxDegree;

	void initialize();
	
	void findLeftTailCutOff();
	void findRightTailCutOff();
	void buildCentralLeftApprox();
	void buildCentralRightApprox();
	void buildLeftApprox();
	void buildRightApprox();

	shared_ptr<ofstream> openFile(std::string fileName);
};

